﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;

namespace WebPartPageParser
{
    /// <summary>
    /// Represents contents of a Data View WebPart page for documentation
    /// </summary>
    public class DVWPPage
    {
        public List<DVWP> DVWPs { get; set; }
        public List<JavaScript> JavaScriptLinks { get; set; }
        public List<CSS> CSSLinks { get; set; }
        public string FileName { get; set; }

        public DVWPPage()
        {
            DVWPs = new List<DVWP>();
            JavaScriptLinks = new List<JavaScript>();
            CSSLinks = new List<CSS>();
        }
    }

    /// <summary>
    /// Represents contents of a Data View WebPart for documentation
    /// </summary>
    public class DVWP
    {
        public string Title { get; set; }
        public List<DataSource> DataSources { get; set; }
        public List<ParameterBinding> ParameterBindings { get; set; }
        public List<XSLImport> XSLImports { get; set; }

        public DVWP()
        {
            DataSources = new List<DataSource>();
            XSLImports = new List<XSLImport>();
            ParameterBindings = new List<ParameterBinding>();
        }

    }

    /// <summary>
    /// XSL Import information
    /// </summary>
    public class XSLImport
    {
        public String Source { get; set; }
    }

    /// <summary>
    /// JavaScript information
    /// </summary>
    public class JavaScript
    {
        public string Source { get; set; }
    }

    /// <summary>
    /// CSS Information
    /// </summary>
    public class CSS
    {
        public string Source { get; set; }
        public string Media { get; set; }
    }

    /// <summary>
    /// DataSource information
    /// </summary>
    public class DataSource
    {
        public string CAMLQuery { get; set; }
        public string WSDLPath { get; set; }
        public string SelectAction { get; set; }
        public string DataSourceType { get; set; }
        public List<DataSourceParameter> DataSourceParameters { get; set; }

        public DataSource()
        {
            DataSourceParameters = new List<DataSourceParameter>();
        }

    }

    /// <summary>
    /// Parameter associated with a data source
    /// </summary>
    public class DataSourceParameter
    {
        public string Name { get; set; }
        public List<KeyValue> Attributes { get; set; }

        public DataSourceParameter()
        {
            Attributes = new List<KeyValue>();
        }
    }

    /// <summary>
    /// Parameter Binding information in a Data View Web Part
    /// </summary>
    public class ParameterBinding
    {
        public string Name { get; set; }
        public string Location { get; set; }
        public string DefaultValue { get; set; }
    }

    /// <summary>
    /// Key/Value class for containing varying parameter information
    /// </summary>
    public class KeyValue
    {
        public string Key { get; set; }
        public string Value { get; set; }
    }

    /// <summary>
    /// Parser program for Data View WebPart page
    /// </summary>
    public class Parser
    {
        /// <summary>
        /// Constants
        /// </summary>
        private const String SOAP = "SOAP";
        private const String LIST = "List";
        private const String SCRIPT_XPATH = "//script";
        private const String LINK_XPATH = "//link";
        private const String DVWP_XPATH = "//*[name()='webpartpages:dataformwebpart']";

        //Const Tags
        private const String SHAREPOINT_SPDATASOURCE = "sharepoint:spdatasource";
        private const String SHAREPOINT_SOAPDATASOURCE = "sharepoint:soapdatasource";
        private const String WEBPARTPAGES_DATAFORMPARAMETER = "webpartpages:dataformparameter";
        private const String ASP_PARAMETER = "asp:parameter";
        private const String ASP_QUERYSTRINGPARAMETER = "asp:querystringparameter";
        private const String DATASOURCES = "datasources";
        private const String PARAMETERBINDINGS = "parameterbindings";
        private const String PARAMETERBINDING = "parameterbinding";
        private const String XSL = "xsl";
        private const String XSLSTYLESHEET = "xsl:stylesheet";
        private const String XSLIMPORT = "xsl:import";
        private const String SHAREPOINT_AGGREGATEDDATASOURCE = "sharepoint:aggregatedatasource";
        private const String SOURCES = "sources";

        //Const Attributes
        private const String TITLE_ATTR = "Title";
        private const String NAME_ATTR = "name";
        private const String SELECT_PARAMETERS_ATTR = "selectparameters";
        private const String WSDL_PATH_ATTR = "wsdlpath";
        private const String SELECT_ACTION_ATTR = "selectaction";
        private const String SELECT_COMMAND_ATTR = "selectcommand";
        private const String DEFAULT_VALUE_ATTR = "defaultvalue";
        private const String LOCATION_ATTR = "location";
        private const String SRC_ATTR = "src";
        private const String HREF_ATTR = "href";
        private const String MEDIA_ATTR = "media";

        /// <summary>
        /// Identify and Set Script Tags
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="tempList"></param>
        private void IdentifyAndSetScripts(HtmlDocument doc, ref DVWPPage tempList)
        {
            var nodes = doc.DocumentNode.SelectNodes(SCRIPT_XPATH); //Run script

            if (nodes != null)
            {
                foreach (HtmlAgilityPack.HtmlNode node in nodes) //Iterate through each node
                {
                    HtmlAttribute attr = node.Attributes.FirstOrDefault(x => x.Name == SRC_ATTR);

                    if (attr != null)//Only if not null
                    {
                        JavaScript tempJS = new JavaScript();

                        if (String.IsNullOrEmpty(attr.Value) == false)// if not null/empty
                        {
                            tempJS.Source = attr.Value;
                            tempList.JavaScriptLinks.Add(tempJS);
                        }
                    }
                }
            }

        }

        /// <summary>
        /// Identify and Set CSS Links
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="tempList"></param>
        private void IdentifyAndSetLinks(HtmlDocument doc, ref DVWPPage tempList)
        {
            var nodes = doc.DocumentNode.SelectNodes(LINK_XPATH);

            if (nodes != null)
            {
                foreach (HtmlAgilityPack.HtmlNode node in nodes)
                {
                    HtmlAttribute href = node.Attributes.FirstOrDefault(x => x.Name == HREF_ATTR);
                    HtmlAttribute media = node.Attributes.FirstOrDefault(x => x.Name == MEDIA_ATTR);

                    CSS tempCSS = new CSS();

                    //href
                    if (href != null)//Only if not null
                    {
                        if (String.IsNullOrEmpty(href.Value) == false)// if not null/empty
                        {
                            tempCSS.Source = href.Value;
                        }
                    }

                    //media
                    if (media != null)//Only if not null
                    {
                        if (String.IsNullOrEmpty(media.Value) == false)// if not null/empty
                        {
                            tempCSS.Media = media.Value;
                        }
                    }

                    //Add CSS
                    tempList.CSSLinks.Add(tempCSS);

                }
            }


        }

        /// <summary>
        /// Identify and Set XSL Imports
        /// </summary>
        /// <param name="xslNode"></param>
        /// <param name="tempList"></param>
        private void RetrieveXSLImports(HtmlNode dvwpNode, ref DVWP tempDVWP)
        {
            HtmlNode xslNode = dvwpNode.ChildNodes.FirstOrDefault(x => x.Name == XSL);

            if (xslNode != null)
            {
                HtmlNode styleSheetNode = xslNode.ChildNodes.FirstOrDefault(x => x.Name == XSLSTYLESHEET);

                if (styleSheetNode != null)
                {

                    //Now check for xsl:import
                    foreach (HtmlNode node in styleSheetNode.Descendants(XSLIMPORT))
                    {
                        XSLImport xslImportTemp = new XSLImport();

                        HtmlAttribute source = node.Attributes[HREF_ATTR];

                        if (source != null)
                        {
                            xslImportTemp.Source = source.Value;
                        }

                        //Add xsl imports
                        tempDVWP.XSLImports.Add(xslImportTemp);

                    }//end foreach
                }// end check for styleSheetNode

            }//end check for xslNode

        }

        /// <summary>
        /// Set Parameter Bindings
        /// </summary>
        /// <param name="dvwp"></param>
        /// <param name="tempDVWP"></param>
        private void SetParameterBindings(HtmlNode node, ref DVWP tempDVWP)
        {
            //Parameter Binders
            foreach (HtmlNode pBNode in node.Descendants(PARAMETERBINDINGS))
            {
                foreach (HtmlNode pBinding in pBNode.Descendants(PARAMETERBINDING))
                {
                    string temp = pBinding.Name;

                    ParameterBinding tempBinding = new ParameterBinding();

                    if (pBinding.Attributes[NAME_ATTR] != null)
                    {
                        tempBinding.Name = pBinding.Attributes[NAME_ATTR].Value;
                    }

                    if (pBinding.Attributes[LOCATION_ATTR] != null)
                    {
                        tempBinding.Location = pBinding.Attributes[LOCATION_ATTR].Value;
                    }

                    if (pBinding.Attributes[DEFAULT_VALUE_ATTR] != null)
                    {
                        tempBinding.DefaultValue = pBinding.Attributes[DEFAULT_VALUE_ATTR].Value;
                    }

                    //Add Binding
                    tempDVWP.ParameterBindings.Add(tempBinding);
                }
            }
        }

        /// <summary>
        /// Retrieve SPDataSource information
        /// </summary>
        /// <param name="dsNode"></param>
        /// <param name="tempList"></param>
        private void SetSPDataSource(HtmlNode spSourceNode, ref DVWP tempDVWP)
        {
            //DataSource variable
            DataSource tempDS = new DataSource();

            tempDS.DataSourceType = LIST;

            HtmlAttribute attr = spSourceNode.Attributes.FirstOrDefault(x => x.Name == SELECT_COMMAND_ATTR);

            if (attr != null)//Only if not null
            {
                if (String.IsNullOrEmpty(attr.Value) == false)// if not null/empty
                {
                    tempDS.CAMLQuery = attr.Value;
                }
            }

            //Retrieve parameters
            RetrieveParameters(spSourceNode, ref tempDS);

            //Add data source
            tempDVWP.DataSources.Add(tempDS);
        }

        /// <summary>
        /// Retrieve SoapDataSource information
        /// </summary>
        /// <param name="dsNode"></param>
        /// <param name="tempList"></param>
        private void SetSOAPDataSource(HtmlNode spSourceNode, ref DVWP tempDVWP)
        {
            //DataSource variable
            DataSource tempDS = new DataSource();

            tempDS.DataSourceType = SOAP;

            HtmlAttribute attr = spSourceNode.Attributes.FirstOrDefault(x => x.Name == WSDL_PATH_ATTR);

            if (attr != null)//Only if not null
            {
                if (String.IsNullOrEmpty(attr.Value) == false)// if not null/empty
                {
                    tempDS.WSDLPath = attr.Value;
                }
            }

            HtmlAttribute attr2 = spSourceNode.Attributes.FirstOrDefault(x => x.Name == SELECT_ACTION_ATTR);

            if (attr2 != null)//Only if not null
            {
                if (String.IsNullOrEmpty(attr2.Value) == false)// if not null/empty
                {
                    tempDS.WSDLPath = attr2.Value;
                }
            }

            //Retrieve parameters
            RetrieveParameters(spSourceNode, ref tempDS);

            //Add data source
            tempDVWP.DataSources.Add(tempDS);
        }

        /// <summary>
        /// Retrieve parameters from data source
        /// </summary>
        /// <param name="spSourceNode"></param>
        /// <param name="tempDS"></param>
        private void RetrieveParameters(HtmlNode spSourceNode, ref DataSource tempDS)
        {
            //Node Grab Parameters from selectparamters
            foreach (HtmlNode selectParameter in spSourceNode.Descendants(SELECT_PARAMETERS_ATTR))
            {
                foreach (HtmlNode parameter in selectParameter.ChildNodes)
                {
                    if (parameter.Name == WEBPARTPAGES_DATAFORMPARAMETER || parameter.Name == ASP_PARAMETER || parameter.Name == ASP_QUERYSTRINGPARAMETER)
                    {
                        //Build temporary Parameter
                        DataSourceParameter tempDSP = new DataSourceParameter();

                        tempDSP.Name = parameter.Name;

                        //Get Parameter Name
                        KeyValue kv = new KeyValue();

                        HtmlAttribute nameValue = parameter.Attributes.FirstOrDefault(x => x.Name == NAME_ATTR);

                        if (nameValue != null)
                        {
                            kv.Key = nameValue.Name;
                            kv.Value = nameValue.Value;
                        }
                        tempDSP.Attributes.Add(kv);


                        HtmlAttribute defaultValue = parameter.Attributes.FirstOrDefault(x => x.Name == DEFAULT_VALUE_ATTR);
                        kv = new KeyValue();

                        if (defaultValue != null)
                        {
                            kv.Key = defaultValue.Name;
                            kv.Value = defaultValue.Value;
                        }

                        //Add key/value to parameter list
                        tempDSP.Attributes.Add(kv);

                        //Now add to datasourceparameter to datasource
                        tempDS.DataSourceParameters.Add(tempDSP);

                    }//end check for paramters
                }//end foreach parameters within select parameters
            }//end foreach select paramters

        }

        /// <summary>
        /// Identify DVWPS on HTML document and build documentation
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="tempList"></param>
        private void IdentifyAndSetDVWPs(HtmlDocument doc, ref DVWPPage tempList)
        {
            //Query For DVWP
            var nodes = doc.DocumentNode.SelectNodes(DVWP_XPATH); //Run script

            if (nodes != null)
            {
                foreach (HtmlAgilityPack.HtmlNode node in nodes) //Iterate through each node
                {
                    DVWP tempDVWP = new DVWP();

                    if (node.Attributes[TITLE_ATTR] != null)
                    {
                        tempDVWP.Title = node.Attributes[TITLE_ATTR].Value;
                    }

                    //Data View Web Part
                    foreach (HtmlNode dsNode in node.Descendants(DATASOURCES))
                    {

                        //check for aggregated
                        HtmlNode aggregateDataSource = dsNode.ChildNodes.FirstOrDefault(x => x.Name == SHAREPOINT_AGGREGATEDDATASOURCE);

                        if (aggregateDataSource != null)//Aggregate Source
                        {
                            //Get sources
                            HtmlNode sourceNode = aggregateDataSource.ChildNodes.FirstOrDefault(x => x.Name == SOURCES);

                            if (sourceNode != null)
                            {
                                //Handle SPDataSource
                                foreach (HtmlNode spSourceNode in sourceNode.Descendants(SHAREPOINT_SPDATASOURCE))
                                {
                                    //Set SP Data Source
                                    SetSPDataSource(spSourceNode, ref tempDVWP);
                                }

                                //Handle SOAP Sources
                                foreach (HtmlNode spSourceNode in sourceNode.Descendants(SHAREPOINT_SOAPDATASOURCE))
                                {
                                    //Set Soap Data Source
                                    SetSOAPDataSource(spSourceNode, ref tempDVWP);
                                }
                            }
                        }
                        else //Single data source list
                        {
                            HtmlNode spSourceNode = dsNode.ChildNodes.FirstOrDefault(x => x.Name == SHAREPOINT_SOAPDATASOURCE || x.Name == SHAREPOINT_SPDATASOURCE);

                            if (spSourceNode != null)
                            {
                                if (spSourceNode.Name == SHAREPOINT_SPDATASOURCE)
                                {
                                    //Set SP Data Source
                                    SetSPDataSource(spSourceNode, ref tempDVWP);
                                }
                                else if (spSourceNode.Name == SHAREPOINT_SOAPDATASOURCE)
                                {
                                    //Set Soap Data Source
                                    SetSOAPDataSource(spSourceNode, ref tempDVWP);
                                }
                            }//end spSourceNode check
                        }

                    }//end foreach

                    //Handle Parameter Bindings
                    SetParameterBindings(node, ref tempDVWP);

                    //Retrieve xsl imports
                    RetrieveXSLImports(node, ref tempDVWP);

                    //Now add DVWP
                    tempList.DVWPs.Add(tempDVWP);
                }// end foreach
            }

        }


        /// <summary>
        /// Parse File based on path supplied
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public DVWPPage ParseFile(string file)
        {
            //Variable to contain parsed file.
            DVWPPage tempList = new DVWPPage();

            //Get filename
            tempList.FileName = file.Substring(file.LastIndexOf('\\') + 1);

            //Open File for parsing
            HtmlDocument doc = new HtmlDocument();
            doc.Load(file);

            //Build Documentation
            IdentifyAndSetScripts(doc, ref tempList);
            IdentifyAndSetLinks(doc, ref tempList);
            IdentifyAndSetDVWPs(doc, ref tempList);
            return tempList;

        }



    }
}
